/*
 * graph.c
 *
 *  Created on: 22/04/2013
 *      Author: jose
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "graph.h"
#include "error.h"
#include "misc.h"


Graph_Adj_List *
adj_list_create_with_urls(const unsigned len, String * urls)
{
	assert(len > 0);
	assert(urls);

	Graph_Adj_List * gr;
	unsigned i;

	gr = (Graph_Adj_List *) calloc(1, sizeof(Graph_Adj_List));
	if (!gr)
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se ha podido crear el grafo");
		return NULL;
	}

	/* Crear headnodes */
	gr->heads = (Headnode *) calloc(len + 1, sizeof(Headnode));
	if (!(gr->heads))
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se han podido crear los headnodes");
		return NULL ;
	}

	/* Inicializar headnodes */
	for (i = 1; i <= len; i++)
	{
		/* Cargar URLs */
		gr->heads[i].url = string_create_with_len(urls[i-1].text, urls[i-1].len);
		if (!(gr->heads[i].url))
		{
			error_message(__FILE__, __FUNCTION__, __LINE__, "No se han podido cargar las URLs");
			return NULL ;
		}
	}

	gr->heads_len = len;
	gr->empty = len + 1;

	return gr;
}



Graph_Adj_List *
adj_list_create_no_urls(const unsigned len)
{
	assert(len > 0);

	Graph_Adj_List * gr;

	gr = (Graph_Adj_List *) calloc(1, sizeof(Graph_Adj_List));
	if (!gr)
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se ha podido crear el grafo");
		return NULL;
	}

	/* Crear headnodes */
	gr->heads = (Headnode *) calloc(len + 1, sizeof(Headnode));
	if (!(gr->heads)) {
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se han podido crear los headnodes");
		return NULL ;
	}

	gr->heads_len = len;
	gr->empty = 1;

	return gr;
}


Graph_Adj_List *
adj_list_update_next_empty_headnode(Graph_Adj_List * gr, const String * str)
{
	assert(gr);
	assert(str);
	assert(gr->empty <= gr->heads_len+1);

	if (gr->empty > gr->heads_len)
		return gr;

	/* Cargar URLs */
	gr->heads[gr->empty].url = string_create_with_len(str->text, str->len);
	if (!(gr->heads[gr->empty].url))
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se han podido cargar la URLs al headnode");
		return NULL ;
	}

	gr->empty++;

	return gr;
}


Node *
headnode_add_node(Graph_Adj_List * gr, const String * url_head, const String * url_to)
{
	assert(gr);
	assert(url_head);
	assert(url_to);

	Node * temp = NULL, * ptr = NULL;
	int index_head, i;

	temp = (Node *) calloc(1, sizeof(Node));
	if (!temp)
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No se han podido crear el nodo");
		return NULL;
	}

	/* buscar headnode correspondiente a la url_head  */
	index_head = adj_list_search_headnode(gr, url_head);
	if (!index_head)
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No existe headnode para la URL head");
		return NULL;
	}

	/* buscar headnode correspondiente a la url_to  */
	i = adj_list_search_headnode(gr, url_to);
	if (!i)
	{
		error_message(__FILE__, __FUNCTION__, __LINE__, "No existe headnode para la URL to");
		return NULL;
	}

	/* Cargar al node el indice del headnode */
	temp->head = i;
	/* Incrementar cantidad de apariciones de la transicion */
	temp->arc++;

	if (gr->heads[index_head].nodes_len)
	{
		/* Recorrer hasta el final */
		for(ptr = gr->heads[index_head].link ; ptr->link ; ptr = ptr->link);
		ptr->link = temp;
	}
	else
		gr->heads[index_head].link = temp;

	/* Incrementar un nodo hijo */
	gr->heads[index_head].nodes_len++;

	return temp;
}


unsigned
adj_list_search_headnode(Graph_Adj_List * gr, const String * url)
{
	assert(gr);
	assert(url);

	unsigned i, end;

	end = (gr->empty > gr->heads_len ? gr->heads_len : gr->empty);

	for (i=1 ; i <= end ; i++)
	{
		/* Para la primera búsqueda, caso que aún no este inicializado el url
		 * en el headnode
		 */
		if (!(gr->heads[i].url))
			continue;

//		if (!memcmp(gr->heads[i].url->text, url->text, gr->heads[i].url->len))
		if (!string_compare(gr->heads[i].url, url))
			return i;
	}

	return 0;
}

/* TODO esto deberia traer una lista enlazada de todos los nodos encontrados, no la primera
 * coincidencia nada mas
 */
Node *
search_node_all_headnodes(Graph_Adj_List * gr, const String *url)
{
	assert(gr);
	assert(url);

	Node * n_ptr = NULL;
	unsigned i, end;

	end = (gr->empty > gr->heads_len ? gr->heads_len : gr->empty);

	for (i=1 ; i <= end ; i++)
	{
		n_ptr = headnode_search_node(gr, i, url);

		if (n_ptr)
			return n_ptr;
	}

	return NULL;
}


Node *
headnode_search_node(Graph_Adj_List * gr, unsigned index, const String * url)
{
	assert(gr);
	assert(url);
	assert(index > 0);
	assert(index <= gr->heads_len);

	Node * n_ptr = NULL;

	for (n_ptr = gr->heads[index].link ; n_ptr ; n_ptr = n_ptr->link)
	{
//		if (!memcmp(gr->heads[n_ptr->head].url->text, url->text, gr->heads[n_ptr->head].url->len))
		if (!string_compare(gr->heads[n_ptr->head].url, url))
			return n_ptr;
	}

	return NULL;
}

void
update_headnode_appareances(Graph_Adj_List * gr, unsigned index)
{
	assert(index > 0);
	assert(index <= gr->empty);

	gr->heads[index].appareances++;
}


void
adj_list_print_headnodes(Graph_Adj_List * gr)
{
	assert(gr);

	unsigned i, end;

	end = (gr->empty > gr->heads_len ? gr->heads_len : gr->empty);

	for (i=1 ; i <= end ; i++)
	{
		printf("\nHead %5u %.*s appr: %u child: %u", i, gr->heads[i].url->len,
				gr->heads[i].url->text, gr->heads[i].appareances, gr->heads[i].nodes_len);
	}
}


void
adj_list_print(Graph_Adj_List * gr)
{
	assert(gr);

	Node * ptr = NULL;
	unsigned i, end;

	end = (gr->empty > gr->heads_len ? gr->heads_len : gr->empty);

	printf("\n\n===== GRAFO - LISTA DE ADYACENCIAS =====");

	for (i=1 ; i <= end ; i++)
	{
		printf("\nHead %5u %.*s Appareances: %u Nodes: %u", i, gr->heads[i].url->len,
				gr->heads[i].url->text, gr->heads[i].appareances, gr->heads[i].nodes_len);

		for(ptr = gr->heads[i].link ; ptr ; ptr = ptr->link)
			printf("\n\t\t%.*s Arc: %u Conf: %f", gr->heads[ptr->head].url->len,
					gr->heads[ptr->head].url->text, ptr->arc, ptr->conf);

	}
}

void
free_graph(Graph_Adj_List * gr)
{
	assert(gr);

	Node * ptr = NULL, * old = NULL;
	unsigned i;

	for(i=1 ; i<=(gr->heads_len) ; i++)
	{
		/* Liberar lista de nodos hijos */
		for(ptr=gr->heads[i].link ; ptr ; )
		{
			old = ptr;
			ptr = old->link;
			free(old);
		}

		/* Liberar URL */
		free(gr->heads[i].url);
	}

	/* Liberar grafo */
	free(gr);

	gr = NULL;
}
